package com.custom.base.http

import android.annotation.SuppressLint
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.custom.base.config.BaseConfig
import com.custom.base.dialog.LoadingDialog
import com.custom.base.dialog.SDMyDialog
import com.custom.base.entity.OkHttpBodyEntity
import com.custom.base.manager.SDActivityManager
import com.custom.base.util.*
import okhttp3.Call
import okhttp3.Headers.Companion.headersOf
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.UnsupportedEncodingException
import java.net.URLEncoder
import java.security.SecureRandom
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager


/**
 * 作者：
 * 时间：2017/6/26
 * 说明：OkHttpManager
 */

class OkHttpManager : OkHttpResoutCallBack.OnHttpFinish {
    private var client: OkHttpClient.Builder? = null
    private var handler: Handler? = null
    private var dialogs: MutableList<LoadingDialog> = mutableListOf()
    private var timerUtil = SDTimerUtil()
    private var isDialoging:Boolean = false
    var headModel:MutableMap<String,String> = mutableMapOf()
    private var httpCode: OkHttpResoutCallBack.OnHttpCode? = null

    /**
     * 初始化请求
     */
    private fun init(){
        if(!SDActivityManager.instance.isEmpty){
            val builder = OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
            val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
                @Throws(CertificateException::class)
                override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
                }

                @Throws(CertificateException::class)
                override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
                }

                override fun getAcceptedIssuers(): Array<X509Certificate> {
                    return arrayOf()
                }
            })
            val sslContext = SSLContext.getInstance("SSL")
            sslContext.init(null, trustAllCerts, SecureRandom())
            val sslSocketFactory = sslContext.socketFactory
            builder.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
            builder.hostnameVerifier { _, _ -> true }
            client = builder
            if (handler==null){
                handler = Handler(Looper.getMainLooper())
            }
        }
    }

    /**
     * 设置 head
     */
    private fun addHead(request: Request.Builder, api: String) {
        val iterator = headModel.iterator()
        while (iterator.hasNext()){
            val baseHeadModel = iterator.next()
            request.addHeader(baseHeadModel.key,baseHeadModel.value)
        }
    }

    /**
     * 异步请求
     * @param formBody 参数
     * @param callback 回调
     * @param isShowDialog 是否请求中显示加载中
     * @param isDelay 是否请求时延迟出现加载中
     * @param delay 延迟时间
     * @param showMessage 加载提示类容
     * @param newUrl 新url
     */
    fun requestInterface(
        formBody: OkHttpFromBoy?,
        callback: OkHttpCallback.Callback?,
        isShowDialog:Boolean = true,
        isDelay:Boolean = true,
        delay:Long = 500,
        newUrl:String? = null
    ) {
        val baseModel = BaseHttpModel(head = headModel)
//        initDialog(isShowDialog, isDelay, delay)
        if(client == null) init()
        val body = OkHttpBodyEntity(baseModel)
        formBody?.addBody(body)
        newUrl?.apply { baseModel.http = this }
        val api = body.getApi()
        val request = Request.Builder().url(newUrl ?: body.getUrl() + "/" + api)
        val bodyBu = body.formBodyBu
        bodyBu?.apply {
            request.post(this)
        }
        addHead(request,api)
        client?.apply {
            handler?.apply {
                build().newCall(request.build()).enqueue(
                    OkHttpResoutCallBack(
                        callback,
                        this,
                        httpFinish = this@OkHttpManager,
                        httpCode = httpCode,
                        direct = newUrl!=null,
                        baseModel = baseModel
                    )
                )
            }
        }
    }

    /**
     * 初始化dialog
     * @param isShowDialog 是否请求中显示加载中
     * @param isDelay 是否请求时延迟出现加载中
     * @param delay 延迟时间
     * @param showMessage 加载提示类容
     */
    private fun initDialog(
        isShowDialog:Boolean,
        isDelay:Boolean,
        delay:Long
    ){
        Log.e("~~~", "initDialog--isShowDialog: $isShowDialog" )
        if(isShowDialog){
            Log.e("~~~", "initDialog--isDelay: $isDelay" )
            if(isDelay){
                Log.e("~~~", "initDialog--isWorking: ${timerUtil.isWorking}" )
                if(timerUtil.isWorking) timerUtil.stopWork()
                isDialoging = true
                timerUtil.startWork(delay,object : SDTimerUtil.SDTimerListener{
                    override fun onWork() {}
                    override fun onWorkMain() {
                        Log.e("~~~", "onWorkMain: $isDialoging" )
                        if(isDialoging) dialogs.add(SDMyDialog.loading())
                    }
                })
            }else dialogs.add(SDMyDialog.loading())
        }
    }

    override fun finish() {
        isDialoging = false
        dialogs.forEach { it.cancel() }
        dialogs.clear()
    }

    /**
     * 同步请求
     * @param formBody 参数
     * @return
     */
    fun requestInterfaceSynchronize(formBody: OkHttpFromBoy,newUrl:String? = null): String? {
        val baseModel = BaseHttpModel(head = headModel)
        if(client == null) init()
        val body = OkHttpBodyEntity(baseModel)
        formBody.addBody(body)
        newUrl?.apply { baseModel.http = this }
        val api = body.getApi()
        val request = Request.Builder().url(newUrl ?: body.getUrl() + "/" + api)
        val bodyBu = body.formBodyBu
        bodyBu?.apply {
            request.post(this)
        }
        addHead(request,api)
        try {
            client?.apply {
                val execute = build().newCall(request.build()).execute()
                val resout = execute.body!!.string()
                baseModel.result = resout
                baseModel.print()
                return resout
            }
            return ""
        } catch (e: IOException) {
            e.printStackTrace()
        }
        baseModel.print()
        return null
    }

    /**
     * 下载
     * @param url
     * @param path
     * @param callback
     */
    fun requestInterfaceDownload(
        url: String,
        path: String,
        callback: OkHttpCallback.ProgressCallback
    ) {
        val baseModel = BaseHttpModel(head = headModel)
        if(client == null) init()
        baseModel.http  = "$url $path"
        val request = Request.Builder().url(url)
        client?.apply {
            handler?.apply {
                build().newCall(request.build()).enqueue(
                    OkHttpResoutCallBack(
                        path,
                        callback,
                        this,
                        baseModel = baseModel
                    )
                )
            }
        }
    }

    fun requestInterfaceDownloadSynchronize(
        url: String,
        path: String,
        callback: OkHttpCallback.ProgressCallback?
    ) {
        val baseModel = BaseHttpModel(head = headModel)
        if(client == null) init()
        baseModel.http = url
        baseModel.print()
        val request = Request.Builder().url(url)
        client?.apply {
            val call = build().newCall(request.build())
            var `is`: InputStream? = null//输入流
            var fos: FileOutputStream? = null//输出流
            try {
                val response = call.execute()
                `is` = response.body!!.byteStream()//获取输入流
                val contentLenght = response.body!!.contentLength()
                if (`is` != null) {
                    val file = File(path)
                    fos = FileOutputStream(file)
                    val buf = ByteArray(1024)
                    var ch = -1
                    var process = 0
                    while (ch != -1){
                        ch = `is`.read(buf)
                        fos.write(buf, 0, ch)
                        process += ch
                        val finalProcess = process
                        callback?.onResponseProgress(
                            (finalProcess * 1.0f / contentLenght * 100).toLong(),
                            100,
                            false
                        )
                    }
                    callback?.onSuccess(file)
                }
                fos!!.flush()
                // 下载完成
                fos.close()
            } catch (e: Exception) {
                callback?.onFailure(e)
            } finally {
                try {
                    `is`?.close()
                } catch (e: IOException) {
                }

                try {
                    fos?.close()
                } catch (e: IOException) {
                }

            }
            if (callback != null) {
                callback.onFinish()
                call.cancel()
            }
        }
    }

    /**
     * 上传
     * @param api
     * @param callback
     * @return
     */
    fun requestInterfaceUpload(api: String, file: File, callback: OkHttpCallback.Callback): Call? {
        val baseModel = BaseHttpModel(head = headModel)
        if(client == null) init()
        baseModel.http = BaseConfig.getInstance.getUrl()+"/"+api
        val builder = OkHttpClient.Builder()
            .connectTimeout(15, TimeUnit.SECONDS)
            .writeTimeout(20, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
        val fileBody = RequestBody.create("multipart/form-data".toMediaTypeOrNull(), file)
        val requestBody = MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("file",file.name,fileBody)
            .build()
        val request = Request.Builder()
            .url(baseModel.http)
            .post(requestBody)
        addHead(request,"")
        val call = builder.build().newCall(request.build())
        handler?.apply {
            call.enqueue(OkHttpResoutCallBack(
                callback,
                this,
                httpFinish = this@OkHttpManager,
                httpCode = httpCode,
                baseModel = baseModel
            ))
        }
        return call
    }

    /**
     * 上传
     * @param url
     * @return
     */
    fun requestInterfaceUploadSynchronize(url: String, map:MutableMap<String,String>, file: File): String? {
        val baseModel = BaseHttpModel(head = headModel)
        if(client == null) init()
        baseModel.http = url
        var name = ""
        try {
            name = URLEncoder.encode(file.name, "UTF-8")
        } catch (e: UnsupportedEncodingException) {
            e.printStackTrace()
            name = RandomUtil.random.toString() + ""
        }

        val builder = OkHttpClient.Builder()
            .connectTimeout(15, TimeUnit.SECONDS)
            .writeTimeout(20, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
        val fileBody = RequestBody.create("application/octet-stream".toMediaTypeOrNull(), file)
        val requestBody = MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addPart(
                headersOf(
                    "Content-Disposition",
                    "form-data; name=\"file\""
                ),
                RequestBody.create(null, "HGR")
            )
            .addPart(
                headersOf(
                    "Content-Disposition",
                    "form-data; name=\"file\"; filename=\"$name\""
                ), fileBody
            )
        for (entry in map.entries) {
            requestBody.addFormDataPart(entry.key,entry.value)
        }
        val request = Request.Builder()
            .url(url)
            .post(requestBody.build())
        addHead(request,url)
        val call = builder.build().newCall(request.build())
        try {
            val execute = call.execute()
            val resout = execute.body!!.string()
            baseModel.result = resout
            baseModel.print()
            return resout
        } catch (e: IOException) {
            e.printStackTrace()
        }
        baseModel.result = "null"
        baseModel.print()
        return null
    }
    /**
     * 全局监听请求code
     */
    fun setHttpCodeListener(httpCode: OkHttpResoutCallBack.OnHttpCode?){
        this.httpCode = httpCode
    }

    companion object {
        @SuppressLint("StaticFieldLeak")
        private var mInstance: OkHttpManager? = null

        val instance: OkHttpManager
            get() {
                if (null == mInstance) {
                    synchronized(OkHttpManager::class.java) {
                        if (null == mInstance) {
                            mInstance =
                                OkHttpManager()
                        }
                    }
                }
                return mInstance!!
            }
    }
}
